#include <iostream>
#include "MongoTypes.h"

using namespace ORB_SLAM2;
using namespace MongoIO::types;

int main()
{
    const mongocxx::instance instance; // This should be done only once.
    const mongocxx::uri uri("mongodb://localhost:27017");
    const mongocxx::client client(uri);
    const mongocxx::database db(client["ORB_SLAM2"]);
    mongocxx::collection coll(db["test"]);

    bsoncxx::builder::stream::document doc;

    coll.drop();

    //! create data
    cv::Mat mat_float = cv::Mat::eye(3,3,CV_32FC1);
    cv::Mat mat_uchar = cv::Mat::eye(3,3,CV_8UC1);
    cv::KeyPoint keyPoint1(1,4,0.6);
    cv::KeyPoint keyPoint2(90,48,1.6);
    std::vector<float> vec_float{0.1, 0.4, 0.7, 0.11, 0.8};
    std::vector<std::vector<unsigned int> > vvec_uint{{1, 2}, {4, 5}};
    std::map<unsigned int, double> bow_vec{{1,0.1}, {2,0.6}, {9,0.8}};
    std::map<uint32_t, std::vector<unsigned int> > bow_feat{{1, {90, 89}}, {2, {1, 3}}, {9, {8, 7}}};
    std::vector<std::vector<std::vector<size_t> > > grids{{{1,2},{3,4,7}},{{4},{0,9},{10,89}},{{1,3},{5,8}}};
    std::vector<cv::KeyPoint> vec_keypoint;
    std::pair<int, int> pair{1,2};
    std::vector<std::pair<int, int> > pairs{{8,2}, {4,5}};
    vec_keypoint.push_back(keyPoint1);
    vec_keypoint.push_back(keyPoint2);

    //! create MongoIO types && write to doc
    doc << "id" << 111
        << "name" << "test"
        << b_keypoint{"keypoint1", keyPoint1}
        << b_keypoint{"keypoint2", keyPoint2}
        << b_vector_float{"vec_float", vec_float}
        << b_vector_uint{"vec_uint", vvec_uint[0]}
        << b_vvector_uint{"vvec_uint", vvec_uint}
        << b_mat{"mat_float", mat_float}
        << b_mat{"mat_uchar", mat_uchar}
        << b_bowvec{"bow_vec", bow_vec}
        << b_vector_keypoint{"vec_keypoint", vec_keypoint}
        << b_bowfeat{"bow_feat", bow_feat}
        << b_grid{"grids", grids}
        << b_pair<int, int>{"pair", pair}
        << b_vector<std::pair<int, int> >{"pairs", pairs};

    std::cout << bsoncxx::to_json(doc.view()) << std::endl;

    //! write to database
    bsoncxx::stdx::optional<mongocxx::result::insert_one> insert_get = coll.insert_one(doc.view());
    bsoncxx::types::b_oid oid = insert_get->inserted_id().get_oid();

    //! find in database
    bsoncxx::stdx::optional<bsoncxx::document::value> result = coll.find_one(doc.view());

    bsoncxx::document::view result_view = result.value().view();

    std::cout << "=== read data from database ===" << std::endl;

    cv::KeyPoint kp1 = b_keypoint::from(result_view["keypoint1"]);
    cv::KeyPoint kp2 = b_keypoint::from(result_view["keypoint2"]);

    std::vector<float> vect1 = b_vector_float::from(result_view["vec_float"]);
    std::vector<unsigned int> vect2 = b_vector_uint::from(result_view["vec_uint"]);
    std::vector<std::vector<unsigned int> > vect3 = b_vvector_uint::from(result_view["vvec_uint"]);
    std::vector<cv::KeyPoint> vect4 = b_vector_keypoint::from(result_view["vec_keypoint"]);

    std::map<unsigned int, double> map1 = b_bowvec::from(result_view["bow_vec"]);

    cv::Mat mat1 = b_mat::from(result_view["mat_float"]);

    cv::Mat mat2 = b_mat::from(result_view["mat_uchar"]);

    std::pair<int, int> pair1 = b_pair<int, int>::from(result_view["pair"]);
    std::vector<std::pair<int, int> > pairs1 = b_vector<std::pair<int, int> >::from(result_view["pairs"]);

    std::cout << "keypoint1: " << kp1.pt.x << " " << kp1.pt.y << " " << kp1.size << std::endl;
    std::cout << "keypoint2: " << kp2.pt.x << " " << kp2.pt.y << " " << kp2.size << std::endl;

    std::cout << "vec_float: ";
    for_each(vect1.begin(), vect1.end(), [](float v){ std::cout << v << " "; });
    std::cout << std::endl;

    std::cout << "vec_keypoint: { ";
    for_each(vect4.begin(), vect4.end(), [](cv::KeyPoint kp){
        std::cout << "{" << kp.pt.x << " " << kp.pt.y << " " << kp.size << "} ";
    });
    std::cout << "}" << std::endl;

    std::cout << "bow_vec: { ";
    for_each(map1.begin(), map1.end(), [](std::pair<unsigned int, double> v){
        std::cout << "{" << v.first << ", " << v.second << "} ";
    });
    std::cout << "}" << std::endl;

    std::cout << "mat_float: \n" << mat1 << std::endl;
    std::cout << "mat_uchar: \n" << mat2 << std::endl;

    std::cout << "pair: " << pair1.first << ", " << pair1.second << std::endl;

    std::cout << "pairs: { ";
    for_each(pairs1.begin(), pairs1.end(), [](std::pair<int, int> v){
      std::cout << "{" << v.first << ", " << v.second << "} ";
    });
    std::cout << "}" << std::endl;

    //! find 选项设置，返回特定的变量。
    mongocxx::options::find find_opts{};
    find_opts.projection(bsoncxx::builder::stream::document{} << "id" << 1 << "vec_float" << open_document
                                                         << "$slice" << open_array << 2 << 2 << close_array
                                                         << close_document << "dfaf" << 9<< finalize);

//    find_opts.projection(bsoncxx::builder::stream::document{}   << "vec_float" << open_document
//                                                         << "$slice" << open_array << 1 << 2 << close_array
//                                                         << close_document  << finalize);

    bsoncxx::builder::stream::document fliter_builder{};
    fliter_builder << "_id" << oid;

    auto cursor = coll.find(fliter_builder.view(), find_opts);
    std::cout << "\n find: \n";
    for (auto &&doc : cursor) {
        std::cout << bsoncxx::to_json(doc) << std::endl;
    }


    const MongoIO::IO::Ptr mongoio{new MongoIO::IO("mongodb://localhost:27017")};

    for(long int i = 0; i < 9999999 ; ++i) {

        std::cout << "i: " << i << std::endl;

        //! 改变数组中的特定位置
        bsoncxx::builder::stream::document update_builder{};
        update_builder << "vec_float.1" << i*0.1 << "vec_float.2" << i*0.2;
        mongoio->UpdateToDataBase("ORB_SLAM2", "test", oid, update_builder.view());

        mongoio->FindFromDataBase("ORB_SLAM2", "test", oid, find_opts, result);
        std::cout << "find: " << bsoncxx::to_json(result.value().view()) << std::endl;
    }


    std::cout<< "\n find one: \n" << bsoncxx::to_json(result.value().view()) << std::endl;

    return 0;

}
